Allow updating to a precise revision
authorAlex Crichton <alex@alexcrichton.com>
Tue, 23 Sep 2014 14:30:16 +0000 (07:30 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Thu, 25 Sep 2014 15:51:45 +0000 (08:51 -0700)
This commit adds a flag, --precise, to cargo update. This flag is used to update
a dependency to precisely an exact revision (or branch) as part of an update
step. For git repositories the argument is some form of reference, while
registry packages this will be a version number.

The flag --precise forces a non-aggressive update and will fail if the
--aggresive flag is specified.

Closes #484

src/bin/update.rs
src/cargo/ops/cargo_generate_lockfile.rs
src/cargo/ops/mod.rs
src/cargo/util/config.rs
tests/test_cargo_compile_git_deps.rs

index f2328a23a6770a99e77f10ce9b4c93b58a15b296..d7fd67b5f56c304db30d76c50346312224252238 100644 (file)
@@ -15,6 +15,7 @@ Usage:
 Options:
     -h, --help              Print this message
     --aggressive            Force updating all dependencies of <name> as well
+    --precise PRECISE       Update a single dependency to exactly PRECISE
     --manifest-path PATH    Path to the manifest to compile
     -v, --verbose           Use verbose output
 
@@ -27,18 +28,32 @@ updated. Its transitive dependencies will be updated only if <spec> cannot be
 updated without updating dependencies.  All other dependencies will remain
 locked at their currently recorded versions.
 
+If PRECISE is specified, then --aggressive must not also be specified. The
+argument PRECISE is a string representing a precise revision that the package
+being updated should be updated to. For example, if the package comes from a git
+repository, then PRECISE would be the exact revision that the repository should
+be updated to.
+
 If <spec> is not given, then all dependencies will be re-resolved and
 updated.
 
 For more information about package ids, see `cargo help pkgid`.
-",  flag_manifest_path: Option<String>, arg_spec: Option<String>)
+",  flag_manifest_path: Option<String>, arg_spec: Option<String>,
+    flag_precise: Option<String>)
 
 pub fn execute(options: Options, shell: &mut MultiShell) -> CliResult<Option<()>> {
     debug!("executing; cmd=cargo-update; args={}", os::args());
     shell.set_verbose(options.flag_verbose);
     let root = try!(find_root_manifest_for_cwd(options.flag_manifest_path));
 
-    ops::update_lockfile(&root, shell, options.arg_spec, options.flag_aggressive)
+    let mut update_opts = ops::UpdateOptions {
+        aggressive: options.flag_aggressive,
+        precise: options.flag_precise.as_ref().map(|s| s.as_slice()),
+        to_update: options.arg_spec.as_ref().map(|s| s.as_slice()),
+        shell: shell,
+    };
+
+    ops::update_lockfile(&root, &mut update_opts)
         .map(|_| None).map_err(|err| CliError::from_boxed(err, 101))
 }
 
index b2132e047cd01aa43681ea636c1c77ec005833a4..f9cde510c6fe9fba6de3e756c162ab07d9afacbf 100644 (file)
@@ -12,6 +12,13 @@ use util::config::{Config};
 use util::{CargoResult, human};
 use util::toml as cargo_toml;
 
+pub struct UpdateOptions<'a> {
+    pub shell: &'a mut MultiShell<'a>,
+    pub to_update: Option<&'a str>,
+    pub precise: Option<&'a str>,
+    pub aggressive: bool,
+}
+
 pub fn generate_lockfile(manifest_path: &Path,
                          shell: &mut MultiShell)
                          -> CargoResult<()> {
@@ -33,9 +40,7 @@ pub fn generate_lockfile(manifest_path: &Path,
 }
 
 pub fn update_lockfile(manifest_path: &Path,
-                       shell: &mut MultiShell,
-                       to_update: Option<String>,
-                       aggressive: bool) -> CargoResult<()> {
+                       opts: &mut UpdateOptions) -> CargoResult<()> {
     let mut source = try!(PathSource::for_path(&manifest_path.dir_path()));
     try!(source.update());
     let package = try!(source.get_root_package());
@@ -47,23 +52,37 @@ pub fn update_lockfile(manifest_path: &Path,
         None => return Err(human("A Cargo.lock must exist before it is updated"))
     };
 
-    let mut config = try!(Config::new(shell, None, None));
+    if opts.aggressive && opts.precise.is_some() {
+        return Err(human("cannot specify both aggressive and precise \
+                          simultaneously"))
+    }
+
+    let mut config = try!(Config::new(opts.shell, None, None));
     let mut registry = PackageRegistry::new(&mut config);
 
-    let sources = match to_update {
+    let mut sources = Vec::new();
+    match opts.to_update {
         Some(name) => {
             let mut to_avoid = HashSet::new();
-            let dep = try!(resolve.query(name.as_slice()));
-            if aggressive {
+            let dep = try!(resolve.query(name));
+            if opts.aggressive {
                 fill_with_deps(&resolve, dep, &mut to_avoid);
             } else {
                 to_avoid.insert(dep);
+                match opts.precise {
+                    Some(precise) => {
+                        sources.push(dep.get_source_id().clone()
+                                        .with_precise(precise.to_string()));
+                    }
+                    None => {}
+                }
             }
-            resolve.iter().filter(|pkgid| !to_avoid.contains(pkgid))
-                   .map(|pkgid| pkgid.get_source_id().clone()).collect()
+            sources.extend(resolve.iter()
+                                  .filter(|p| !to_avoid.contains(p))
+                                  .map(|p| p.get_source_id().clone()));
         }
-        None => package.get_source_ids(),
-    };
+        None => sources.extend(package.get_source_ids().into_iter()),
+    }
     try!(registry.add_sources(sources));
 
     let resolve = try!(resolver::resolve(package.get_summary(),
index 67e30def8a77c3b7642a0baa91e1a427fbe84adf..c3c584ac318b220a034760302de8efb43659621d 100644 (file)
@@ -7,6 +7,7 @@ pub use self::cargo_new::{new, NewOptions};
 pub use self::cargo_doc::{doc, DocOptions};
 pub use self::cargo_generate_lockfile::{generate_lockfile, write_resolve};
 pub use self::cargo_generate_lockfile::{update_lockfile, load_lockfile};
+pub use self::cargo_generate_lockfile::UpdateOptions;
 pub use self::cargo_test::{run_tests, run_benches, TestOptions};
 pub use self::cargo_package::package;
 pub use self::cargo_upload::{upload, upload_configuration, UploadConfig};
index 0d21145a226554b1d1411a539ea2b359338bb5e0..5610369af2d8e0b659c3b344db9e6835209b1ba3 100644 (file)
@@ -144,13 +144,13 @@ impl ConfigValue {
                         toml::String(val) => Ok((val, path.clone())),
                         _ => Err(internal("")),
                     }
-                }).collect::<Result<_, _>>())))
+                }).collect::<CargoResult<_>>())))
             }
             toml::Table(val) => {
                 Ok(Table(try!(val.into_iter().map(|(key, value)| {
                     let value = raw_try!(ConfigValue::from_toml(path, value));
                     Ok((key, value))
-                }).collect::<Result<_, _>>())))
+                }).collect::<CargoResult<_>>())))
             }
             _ => return Err(internal(""))
         }
index 34eeadd1815766be8a8f29d191bdfe8adbfc6474..283113bacc1cfbfee288d3087c8c672cdadc5e39 100644 (file)
@@ -681,13 +681,23 @@ test!(update_with_shared_deps {
         pub fn bar() { println!("hello!"); }
     "#).assert();
     let repo = git2::Repository::open(&git_project.root()).unwrap();
+    let old_head = repo.head().unwrap().target().unwrap();
     add(&repo);
     commit(&repo);
 
     timer::sleep(Duration::milliseconds(1000));
 
+    // By default, not transitive updates
     assert_that(p.process(cargo_dir().join("cargo")).arg("update").arg("dep1"),
                 execs().with_stdout(""));
+
+    // Specifying a precise rev to the old rev shouldn't actually update
+    // anything because we already have the rev in the db.
+    assert_that(p.process(cargo_dir().join("cargo")).arg("update").arg("bar")
+                 .arg("--precise").arg(old_head.to_string()),
+                execs().with_stdout(""));
+
+    // Updating aggressively should, however, update the repo.
     assert_that(p.process(cargo_dir().join("cargo")).arg("update").arg("dep1")
                  .arg("--aggressive"),
                 execs().with_stdout(format!("{} git repository `{}`",